#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using Alone.GameObjects;
using Alone.GameObjects.Enemy;
using Alone.Handlers;
using Alone.Particlesystem;
using Alone.Screens;
using AloneLibrary;
using AloneLibrary.Particlesystem;
using AloneLibrary.Services.Camera;
using AloneLibrary.Services.Input;
using AloneLibrary.Services.Sound;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Net;

#endregion

namespace Alone {
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen {

        #region Fields

        private PacketWriter packetWriter = new PacketWriter();
        private PacketReader packetReader = new PacketReader();
        private NetworkSession networkSession;
        private ContentManager content;
        private SpriteFont gameFont;
        private Vector2 playerPosition = new Vector2(100, 100);
        private Vector2 enemyPosition = new Vector2(100, 100);
        private Random random = new Random();
        private Matrix world = Matrix.Identity;
        private BasicEffect effect;
        private ICameraHandler camera;
        private EnvironmentHandler envHandler;
        private PlayerHandler playerHandler;
        private IInputHandler input;
        private BallisticsHandler ballisticsHandler;
        private EnemyHandler enemyHandler;
        private ParticleSystem smokeParticles;
        private ParticleSystem fireParticles;
        private int timeSinceLastUpdate = 0;
        private int updateInterval = 100;
        private FPS fps;
        private ISoundManager soundHandler;

        #endregion

        #region Properties


        /// <summary>
        /// The logic for deciding whether the game is paused depends on whether
        /// this is a networked or single player game. If we are in a network session,
        /// we should go on updating the game even when the user tabs away from us or
        /// brings up the pause menu, because even though the local player is not
        /// responding to input, other remote players may not be paused. In single
        /// player modes, however, we want everything to pause if the game loses focus.
        /// </summary>
        new bool IsActive {
            get {
                if (networkSession == null) {
                    // Pause behavior for single player games.
                    return base.IsActive;
                } else {
                    // Pause behavior for networked games.
                    return !IsExiting;
                }
            }
        }


        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen(NetworkSession networkSession) {
            this.networkSession = networkSession;

            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);
        }

        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent() {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            gameFont = content.Load<SpriteFont>("gamefont");

            effect = new BasicEffect(ScreenManager.GraphicsDevice, null);

            this.camera = (ICameraHandler)ScreenManager.Game.Services.GetService(typeof(ICameraHandler));
            this.input = (IInputHandler)ScreenManager.Game.Services.GetService(typeof(IInputHandler));

            this.soundHandler = (ISoundManager)ScreenManager.Game.Services.GetService(typeof(ISoundManager));
            this.playerHandler = new PlayerHandler(ScreenManager.Game, networkSession, ScreenManager);
            this.envHandler = new EnvironmentHandler(ScreenManager.Game);
            this.enemyHandler = new EnemyHandler(ScreenManager.Game);
            this.ballisticsHandler = new BallisticsHandler(ScreenManager.Game, ScreenManager);
            this.smokeParticles = new SmokePlumeParticleSystem(ScreenManager.Game, ScreenManager.Game.Content);
            this.fireParticles = new FireParticleSystem(ScreenManager.Game, ScreenManager.Game.Content);
            this.fps = new FPS(ScreenManager.Game);

            ScreenManager.Game.Components.Add(fps);
            ScreenManager.Game.Components.Add(envHandler);
            ScreenManager.Game.Components.Add(playerHandler);
            ScreenManager.Game.Components.Add(enemyHandler);
            ScreenManager.Game.Components.Add(ballisticsHandler);
            ScreenManager.Game.Components.Add(smokeParticles);
            ScreenManager.Game.Components.Add(fireParticles);
            //soundHandler.Play("menu");

            camera.FocusedObject = playerHandler.Player;

            ScreenManager.Game.ResetElapsedTime();
        }

        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent() {
            ScreenManager.Game.Components.Remove(envHandler);
            ScreenManager.Game.Components.Remove(playerHandler);
            ScreenManager.Game.Components.Remove(enemyHandler);

            content.Unload();
        }
        #endregion

        #region Update and Draw
        /// <summary>
        /// Updates the state of the game.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="otherScreenHasFocus"></param>
        /// <param name="coveredByOtherScreen"></param>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen) {
            timeSinceLastUpdate += gameTime.ElapsedGameTime.Milliseconds;
            if (timeSinceLastUpdate >= updateInterval) {
                base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
                if (networkSession != null) {
                    networkSession.Update();

                    //Sends the localplayer's data to everyone in the network
                    foreach (LocalNetworkGamer gamer in networkSession.LocalGamers) {
                        UpdateLocalGamer(gamer);
                    }

                    //Read data from network
                    foreach (LocalNetworkGamer gamer in networkSession.LocalGamers) {
                        ReadIncomingPackets(gamer);
                    }
                }

                if (IsActive) {
                    playerHandler.Enabled = true;
                    enemyHandler.Enabled = true;
                    envHandler.Enabled = true;
                    playerHandler.Weapon.Enabled = true;
                } else {
                    playerHandler.Enabled = false;
                    enemyHandler.Enabled = false;
                    envHandler.Enabled = false;
                    playerHandler.Weapon.Enabled = false;
                }

                // If we are in a network game, check if we should return to the lobby.
                if ((networkSession != null) && !IsExiting) {
                    if (networkSession.SessionState == NetworkSessionState.Lobby) {
                        LoadingScreen.Load(ScreenManager, true, null,
                                           new BackgroundScreen(),
                                           new LobbyScreen(networkSession));
                    }
                }

                timeSinceLastUpdate -= updateInterval;
                smokeParticles.AddParticle(new Vector3(1053f, -197f, -42f), Vector3.Zero);
                fireParticles.AddParticle(new Vector3(1053f, -197f, -42f), Vector3.Zero);

                smokeParticles.AddParticle(new Vector3(667f, -392f, 165f), Vector3.Zero);
                fireParticles.AddParticle(new Vector3(667f, -392f, 165f), Vector3.Zero);

                smokeParticles.AddParticle(new Vector3(981f, -333f, 346f), Vector3.Zero);
                fireParticles.AddParticle(new Vector3(981f, -333f, 346), Vector3.Zero);
            }

            if (playerHandler.AnyPlayerDied) {
                LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen("GameOverBackground"), new GameOverScreen(networkSession));
            }
        }

        /// <summary>
        /// After creating or joining a network session, we must subscribe to
        /// some events so we will be notified when the session changes state.
        /// </summary>
        void HookSessionEvents() {
            networkSession.GamerJoined += GamerJoinedEventHandler;
            networkSession.SessionEnded += SessionEndedEventHandler;
        }


        /// <summary>
        /// This event handler will be called whenever a new gamer joins the session.
        /// We use it to allocate a Tank object, and associate it with the new gamer.
        /// </summary>
        void GamerJoinedEventHandler(object sender, GamerJoinedEventArgs e) {
            int gamerIndex = networkSession.AllGamers.IndexOf(e.Gamer);
        }

        /// <summary>
        /// Event handler notifies us when the network session has ended.
        /// </summary>
        void SessionEndedEventHandler(object sender, NetworkSessionEndedEventArgs e) {
            networkSession.Dispose();
            networkSession = null;
        }

        /// <summary>
        /// Helper for updating a locally controlled gamer.
        /// </summary>
        void UpdateLocalGamer(LocalNetworkGamer gamer) {
            packetWriter.Write(playerHandler.Player.Position);
            packetWriter.Write(playerHandler.Player.Rotation.Y);
            packetWriter.Write(playerHandler.Player.Hp);
            if (networkSession.IsHost)
                foreach (Enemy enemy in enemyHandler.Enemies) {
                    packetWriter.Write(enemy.Position);
                    packetWriter.Write(enemy.Rotation);
                }


            // Send the data to everyone in the session.
            gamer.SendData(packetWriter, SendDataOptions.InOrder);
        }


        /// <summary>
        /// Helper for reading incoming network packets.
        /// </summary>
        void ReadIncomingPackets(LocalNetworkGamer gamer) {
            // Keep reading as long as incoming packets are available.
            while (gamer.IsDataAvailable) {
                NetworkGamer sender;

                // Read a single packet from the network.
                gamer.ReceiveData(packetReader, out sender);

                // Discard packets sent by local gamers
                if (sender.IsLocal) {
                    continue;
                }

                BasePlayer remotePlayer = sender.Tag as BasePlayer;

                playerHandler.RemotePlayer.Position = packetReader.ReadVector3();
                playerHandler.RemotePlayer.Rotation.Y = packetReader.ReadSingle();
                playerHandler.RemotePlayer.Hp = packetReader.ReadInt32();

                if (!networkSession.IsHost) {
                    foreach (Enemy enemy in enemyHandler.Enemies) {
                        enemy.Position = packetReader.ReadVector3();
                        enemy.Rotation = packetReader.ReadVector2();
                    }
                }
            }
        }

        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime) {
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target, Color.Black, 0, 0);

            smokeParticles.SetCamera(camera.View, camera.Projection);
            fireParticles.SetCamera(camera.View, camera.Projection);
            if (TransitionPosition > 0)
                ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);

            base.Draw(gameTime);
        }
        #endregion
    }
}